package com.free.md5parser.controller;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.free.md5parser.entity.APIResponse;
import com.free.md5parser.service.RocksDBService;
import com.free.md5parser.task.ClearFileTask;
import com.free.md5parser.util.AES;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.http.MediaType;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

@Slf4j
@Api(tags = "MD5")
@RestController
@RequestMapping("/api/md5")
public class Md5Controller implements ApplicationRunner {

    @Value("${temp.dir:/data}")
    private String tempDir;

    @Resource
    private RocksDBService rocksDBService;

    @Resource
    private ClearFileTask clearFileTask;

    private Long timestamp;

    @ApiOperation("上传文件")
    @PostMapping("/upload")
    public APIResponse upload(@RequestPart("file") MultipartFile file,
                              @RequestPart("time") String time) throws Exception {
        if (!checkLicense(time)) {
            return new APIResponse(500, null, "认证已过期");
        }
        log.info("开始处理文件......");
        int i = 0;
        try {
            String filePath = getFilePath(file.getOriginalFilename());
            IoUtil.copy(file.getInputStream(), new FileOutputStream(filePath));
            List<String> lines = FileUtil.readUtf8Lines(new File(filePath));
            int size = lines.size();
            File newFile = new File(getFilePath(time + ".txt"));
            List<String> newLines = new ArrayList<>();
            for (String line : lines) {
                String value = rocksDBService.get(line);
                newLines.add(line + "\t" + value);
                i++;
                if (i % 10000 == 0) {
                    log.info("处理进度：" + i + "/" + size);
                }
            }
            FileUtil.appendUtf8Lines(newLines, newFile);
            log.info("处理完成");
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return new APIResponse(500, null, "处理文件失败，请稍后重试");
        }
        return new APIResponse(200, "上传成功", null);
    }

    @ApiOperation("下载文件")
    @GetMapping("/download")
    public void download(@RequestParam("time") String time,
                         @RequestParam("filename") String filename,
                         HttpServletResponse response) throws Exception {
        String filePath = getFilePath(time + ".txt");
        File file = new File(filePath);
        if (file.exists()) {
            FileInputStream fis = null;
            try {
                fis = new FileInputStream(file);
                response.reset();
                response.setCharacterEncoding("UTF-8");
                response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
                String encodedFileName = URLEncoder.encode(filename, "UTF-8");
                response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodedFileName);
                IoUtil.copy(fis, response.getOutputStream());
            } finally {
                if (fis != null) {
                    fis.close();
                }
            }
        }
    }

    @ApiOperation("初始化")
    @GetMapping("/init")
    public String init(@RequestParam("prefix") String prefix) throws Exception {
        String[] split = prefix.split(",");
        for (String s : split) {
            long start = Long.parseLong(StrUtil.padAfter(s, 11, '0'));
            long end = Long.parseLong(StrUtil.padAfter(s, 11, '9'));
            for (long i = start; i <= end; i++) {
                rocksDBService.put(DigestUtil.md5Hex(String.valueOf(i)), String.valueOf(i));
                if (i % 1000000 == 0) {
                    log.info("初始化进度：" + i);
                }
            }
        }
        log.info("初始化完成");
        return "初始化完成";
    }

    @ApiOperation("初始化校验")
    @GetMapping("/init/check")
    public String initCheck(@RequestParam("prefix") String prefix) throws Exception {
        List<String> newLines = new ArrayList<>();
        String[] split = prefix.split(",");
        for (String s : split) {
            long end = Long.parseLong(StrUtil.padAfter(s, 11, '9'));
            String value = rocksDBService.get(DigestUtil.md5Hex(String.valueOf(end)));
            if (StringUtils.endsWithIgnoreCase(value, String.valueOf(end))) {
                newLines.add(s + ":true");
            } else {
                newLines.add(s + ":false");
            }
        }
        return newLines.toString();
    }

    @ApiOperation("处理数据")
    @GetMapping("/process")
    public APIResponse process(@RequestParam("md5") String md5,
                               @RequestParam("signature") String signature) throws Exception {
        if (!checkSignature(signature)) {
            return new APIResponse(500, null, "签名校验失败");
        }
        String time = AES.aesDecode(signature);
        if (!checkLicense(time)) {
            return new APIResponse(500, null, "认证已过期");
        }
        List<String> result = new ArrayList<>();
        String[] split = md5.split(",");
        for (String s : split) {
            String value = rocksDBService.get(s);
            result.add(s + "," + value);
        }
        return new APIResponse(200, result, null);
    }

    @ApiOperation("license")
    @GetMapping("/license")
    public String license(@RequestParam("time") String time) {
        this.timestamp = Long.valueOf(time);
        FileUtil.writeBytes(String.valueOf(timestamp * 5).getBytes(), getLicensePath());
        return "上传成功";
    }

    @ApiOperation("清理文件")
    @GetMapping("/clear")
    public String clearFile() {
        clearFileTask.clearTask();
        return "清理成功";
    }

    @Override
    public void run(ApplicationArguments args) {
        File license = new File(getLicensePath());
        if (license.exists() && license.isFile()) {
            this.timestamp = Long.valueOf(FileUtil.readUtf8String(license)) / 5;
        }
        File fileDir = new File(getFileDirPath());
        if (!fileDir.exists()) {
            fileDir.mkdir();
        }
    }

    private boolean checkLicense(String time) {
        return StrUtil.isNotBlank(time)
                && timestamp != null
                && Long.valueOf(time) < timestamp;
    }

    private boolean checkSignature(String signature) {
        String decode = AES.aesDecode(signature);
        if (StrUtil.isNotBlank(decode)
                && StrUtil.isNumeric(decode)) {
            long time = Long.valueOf(decode);
            long now = System.currentTimeMillis();
            if (now - 5 * 60 * 1000 <= time
                    && time <= now + 5 * 60 * 1000) {
                return true;
            }
        }
        return false;
    }

    private String getFilePath(String filename) {
        return getFileDirPath() + "/" + filename;
    }

    private String getFileDirPath() {
        return tempDir + "/file";
    }

    private String getLicensePath() {
        return tempDir + "/license.txt";
    }
}
